/*
 *   Copyright 1999-2018 dragonshard.net.
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *        http://www.apache.org/licenses/LICENSE-2.0
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 */
package net.dragonshard.dsf.core.toolkit;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.Arrays;
import java.util.List;

/**
 * <p>
 * ClassUtils
 * </p>
 *
 * @author Caratacus
 * @author HCL
 */
public final class ClassUtils {

  private static final char PACKAGE_SEPARATOR = '.';

  /**
   * 代理 class 的名称
   */
  private static final List<String> PROXY_CLASS_NAMES = Arrays.asList("net.sf.cglib.proxy.Factory"
    // cglib
    , "org.springframework.cglib.proxy.Factory"
    , "javassist.util.proxy.ProxyObject"
    // javassist
    , "org.apache.ibatis.javassist.util.proxy.ProxyObject");

  private ClassUtils() {
  }

  /**
   * <p>
   * 判断是否为代理对象
   * </p>
   *
   * @param clazz 传入 class 对象
   * @return 如果对象class是代理 class，返回 true
   */
  public static boolean isProxy(Class<?> clazz) {
    if (clazz != null) {
      for (Class<?> cls : clazz.getInterfaces()) {
        if (PROXY_CLASS_NAMES.contains(cls.getName())) {
          return true;
        }
      }
    }
    return false;
  }

  /**
   * <p>
   * 获取当前对象的 class
   * </p>
   *
   * @param clazz 传入
   * @return 如果是代理的class，返回父 class，否则返回自身
   */
  public static Class<?> getUserClass(Class<?> clazz) {
    return isProxy(clazz) ? clazz.getSuperclass() : clazz;
  }

  /**
   * <p>
   * 获取当前对象的class
   * </p>
   *
   * @param object 对象
   * @return 返回对象的 user class
   */
  public static Class<?> getUserClass(Object object) {
    Assert.notNull(object, "Error: Instance must not be null");
    return getUserClass(object.getClass());
  }

  /**
   * <p>
   * 根据指定的 class ， 实例化一个对象，根据构造参数来实例化
   * </p>
   * <p>
   * 在 java9 及其之后的版本 Class.newInstance() 方法已被废弃
   * </p>
   *
   * @param clazz 需要实例化的对象
   * @param <T> 类型，由输入类型决定
   * @return 返回新的实例
   */
  public static <T> T newInstance(Class<T> clazz) {
    try {
      Constructor<T> constructor = clazz.getDeclaredConstructor();
      constructor.setAccessible(true);
      return constructor.newInstance();
    } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
      throw ExceptionUtils.get("An error occurred while instantiating the object, try adding a no-argument constructor to %s", e, clazz.getName());
    }
  }

  /**
   * <p>
   * 请仅在确定类存在的情况下调用该方法
   * </p>
   *
   * @param name 类名称
   * @return 返回转换后的 Class
   */
  public static Class<?> toClassConfident(String name) {
    try {
      return Class.forName(name);
    } catch (ClassNotFoundException e) {
      throw ExceptionUtils.get("The specified class could not be found!", e);
    }
  }


  /**
   * Determine the name of the package of the given class, e.g. "java.lang" for the {@code
   * java.lang.String} class.
   *
   * @param clazz the class
   * @return the package name, or the empty String if the class is defined in the default package
   */
  public static String getPackageName(Class<?> clazz) {
    Assert.notNull(clazz, "Class must not be null");
    return getPackageName(clazz.getName());
  }

  /**
   * Determine the name of the package of the given fully-qualified class name, e.g. "java.lang" for
   * the {@code java.lang.String} class name.
   *
   * @param fqClassName the fully-qualified class name
   * @return the package name, or the empty String if the class is defined in the default package
   */
  public static String getPackageName(String fqClassName) {
    Assert.notNull(fqClassName, "Class name must not be null");
    int lastDotIndex = fqClassName.lastIndexOf(PACKAGE_SEPARATOR);
    return (lastDotIndex != -1 ? fqClassName.substring(0, lastDotIndex) : "");
  }

  /**
   * 判断class是否存在，不会初始化静态变量
   *
   * @param name 类的全路径
   * @return true 存在, false 不存在
   */
  public static boolean isPresent(String name) {
    try {
      Thread.currentThread().getContextClassLoader().loadClass(name);
      return true;
    } catch (ClassNotFoundException e) {
      return false;
    }
  }
}
